/**
 * Tencent is pleased to support the open source community by making KOALA available.
 * Copyright (C) 2014 THL A29 Limited, a Tencent company. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
 */


/**
 * common
 */

'use strict';

var fs   = require("fs"),
    path = require('path');

/**
 * create a random  string
 * @param  {Number} customSize string size
 * @return {String}            random string
 */
exports.createRdStr = function (customSize) {
    var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
    var size = customSize || 8;
    size = size < 1 ? 8 : size; size = size > chars.length ? 8 : size;

    var s = '';
    for (var i = 0; i < size; i++) {
        var rnum = Math.floor(Math.random() * chars.length);
        s += chars.substring(rnum, rnum + 1);
    }

    return s;
};

/**
 * Wraps the object with an array if its not an array.
 * @param  {Object} objectOrArray the object to wrap, or array.
 * @param  {Object} def           the default value to be returned if objectOrArray is falsy. (default `[]`)
 * @return {Array}                the output array or "def".
 */
exports.asArray = function (objectOrArray, def) {
    if (!objectOrArray) {
        return def || [];
    }
    return Array.isArray(objectOrArray) ? objectOrArray : [objectOrArray];
};

/**
 * checks if the given argument is empty ({} or []).
 * @param  {Object}  objectOrArray the object or array to check.
 * @return {Boolean}               true if empty ({} or []), false otherwise.
 */
exports.isEmpty = function (objectOrArray) {
    return !objectOrArray || (Array.isArray(objectOrArray) ? objectOrArray : Object.keys(objectOrArray)).length === 0;
};

/**
 * test if is empty object
 * @param  {Object}  obj
 * @return {Boolean}
 */
exports.isEmptyObject = exports.isEmpty;

/**
 * test if is object
 * @param  {Object}  obj [description]
 * @return {Boolean}     [description]
 */
exports.isObject = function (obj) {
    return typeof obj === "object";
};

/**
 * replace Json Comments
 * @param  {String} content Json content
 * @return {String}         result
 */
exports.replaceJsonComments = function (content) {
    if (!content) return '';
    return content.replace(/\".+?\"|\'.+?\'/g, function (s) {
        return s.replace(/\/\//g, '@_@');
    }).replace(/\s*?\/\/.*?[\n\r]|[\t\r\n]/g, '').replace(/\/\*[\s\S]+?\*\//g, '').replace(/@_@/g, '//');
};

/**
 * parse JSON
 * @param  {String} content
 * @return {Object}
 */
exports.parseJSON = function (content) {
    content = exports.replaceJsonComments(content);
    try {
        return JSON.parse(content);
    } catch (e) {
        return null;
    }
};

/**
 * read json file sync
 * @param  {String} file file path
 * @return {Object}      json object
 */
exports.readJsonSync = function (file) {
    var content = fs.readFileSync(file, 'utf8');
    return exports.parseJSON(content);
};

/**
 * format date object to string
 * @param  {Object} date date object
 * @param  {String} fmt  format string
 * @return {String}      result string
 * example: dateFormat(new Date(), "yyyy-MM-dd hh:mm:ss")
 */
exports.dateFormat = function (date, fmt) {
    var o = {
        "M+": date.getMonth() + 1, //月份
        "d+": date.getDate(), //日
        "h+": date.getHours(), //小时
        "m+": date.getMinutes(), //分
        "s+": date.getSeconds(), //秒
        "q+": Math.floor((date.getMonth() + 3) / 3), //季度
        "S": date.getMilliseconds() //毫秒
    };
    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
    for (var k in o)
    if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
    return fmt;
};

/**
 * Check Upgrade
 * @param  {String}   upgradeUrl     upgrade post url
 * @param  {String}   currentVersion
 * @param  {Function} callback
 */
exports.checkUpgrade = function (upgradeUrl, currentVersion, callback, events) {
    events = events || {
        // success: function () {},
        // fail: function () {}
    };

    upgradeUrl += '?' + exports.createRdStr();

    jQuery.getJSON(upgradeUrl).done(function (data) {
        if (events.success) events.success();
        versionDetect(data);
    }).fail(function () {
        if (events.fail) events.fail();
    });

    function versionDetect (data) {
        if (!data) {
            callback(null, false);
            return false;
        }

        //not support this platform
        if (data.platform && data.platform.join(',').indexOf(exports.getPlatformName()) === -1) {
            callback(data, false);
            return false;
        }

        var current = getVersionNum(currentVersion),
            target = getVersionNum(data.version),
            hasNewVersion = false;

        if (target.stable > current.stable) hasNewVersion = true;

        if (target.stable === current.stable) {
            if (!target.beta && current.beta) hasNewVersion = true;
            if (target.beta > current.beta) hasNewVersion = true
        }

        if (callback) {
            callback(data, hasNewVersion);
        }
    }

    function getVersionNum(version) {
        var versionInfo = version.split('-beta'),
            betaNum = versionInfo[1],
            numList = versionInfo[0].split('.'),
            stableNum = 0,
            multiple = 100;

        for (var i = 0;i < 3; i++) {
            if (numList[i] !== undefined) {
                stableNum += numList[i] * multiple;
                multiple = multiple / 10;
            }
        }

        return {
            stable: stableNum,
            beta:  betaNum
        };
    }
};

/**
 * get platform name
 */
exports.getPlatformName = function () {
    var names = {
        'win32': 'windows',
        'darwin': 'mac',
        'linux': 'linux'
    };

    return names[process.platform];
};

/**
 * download file use nodejs
 * @param  {String} fileUrl     file url
 * @param  {String} downloadDir Save dir
 * @param  {Function} success
 * @param  {Function} fail
 */
exports.downloadFile = function (fileUrl, downloadDir, success, fail) {
    // Dependencies
    var url = require('url'),
        http = require('http');

    var urlObj = url.parse(fileUrl);
    var options = {
        host: urlObj.host,
        port: urlObj.port || 80,
        path: urlObj.pathname
    };

    if (!fs.existsSync(downloadDir)) fs.mkdirSync(downloadDir);
    var file = fs.createWriteStream(path.join(downloadDir, urlObj.pathname.split('/').pop()));

    http.get(options, function (res) {
        if (!/200|201/.test(res.statusCode)) {
            fail('File not found!');
            return false;
        }

        res.on('data', function (data) {
            file.write(data);
        }).on('end', function () {
            file.end();
            success(file.path);
        });

    }).on('error',function (e) {
        fail(e.message);
    });
};

/**
 * sync two object propertys
 * @param  {object}   source     source object
 * @param  {object}   tmpl     tmpl object
 * @param  {function} callback 
 */
exports.syncObject = function (source, tmpl, callback) {
    var syncAble = false, j, i;
    
    for (j in tmpl) {
        if (source[j] === undefined) {
            source[j] = tmpl[j];
            syncAble = true;
        } else {
            if (exports.isObject(source[j])) {
                for (i in tmpl[j]) {
                    if (source[j][i] === undefined) {
                        source[j][i] = tmpl[j][i];
                        syncAble = true;
                    }
                }
            }
        }
    }
    
    return syncAble;
}

/**
 * Clone Object
 * @param  {object} obj source object
 * @return {object}     new object
 */
exports.clone = function (obj) {
    try {
        return JSON.parse(JSON.stringify(obj));
    } catch (e) {
        return null;
    }
}

/**
 * parse version string
 * @param  {string} version version string
 * @return {number}         version num
 */
exports.parseVersion = function (version) {
    var numList = version.split('.'),
        versionNum = 0,
        multiple = 100;

    for (var i = 0; i < 3; i++) {
        if (numList[i] !== undefined) {
            versionNum += numList[i] * multiple;
            multiple = multiple / 10;
        }
    }

    return versionNum;
}
